package com.flyqiu.flow.core.handler;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.flyqiu.flow.api.context.FlyQiuFlowGlobalContext;
import com.flyqiu.flow.core.DefaultProcInstContext;
import com.flyqiu.flow.core.impl.ru.FlowRuVariableServiceImpl;
import com.flyqiu.flow.core.tools.EventTools;
import com.flyqiu.flow.core.tools.ProcSequenceCompute;
import com.flyqiu.flow.core.var.VariableScope;
import com.flyqiu.common.bean.FlyQiuBeanManage;
import com.flyqiu.flow.api.context.ProcInstContext;
import com.flyqiu.flow.api.element.FlyQiuFlowElementManage;
import com.flyqiu.flow.api.script.FlyQiuFlowVarSerializeManage;
import com.flyqiu.flow.api.std.ElementStartHandler;
import com.flyqiu.flow.common.std.ActivityHandlerResult;
import com.flyqiu.common.tools.StringTools;
import com.flyqiu.flow.event.ProcInstEvent;
import com.flyqiu.flow.event.ProcInstEventContext;
import com.flyqiu.flow.mapper.HiProcInstMapper;
import com.flyqiu.flow.mapper.ProcRuActivityMapper;
import com.flyqiu.flow.mapper.RuProcInstMapper;
import com.flyqiu.flow.bpmn.element.EndEvent;
import com.flyqiu.flow.bpmn.element.ProcessModel;
import com.flyqiu.flow.bpmn.element.SequenceFlow;
import com.flyqiu.flow.bpmn.element.SubProcessModel;
import com.flyqiu.flow.model.entity.base.Variable;
import com.flyqiu.flow.model.entity.hi.ProcHiInst;
import com.flyqiu.flow.model.entity.ru.ProcRuActivity;
import com.flyqiu.flow.model.entity.ru.ProcRuInst;


import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

public class EndEventHandler extends BaseProcActElementHandler<EndEvent, ProcRuActivity> {

    private final ProcInstEventDispatch dispatch = new ProcInstEventDispatch();

    /**
     * @param currentProcInst
     * @param ruActivity
     */
    @Override
    protected ActivityHandlerResult<ProcRuActivity> startElementInst(ProcRuInst currentProcInst, ProcRuActivity ruActivity) {
        ActivityHandlerResult<ProcRuActivity> result = new ActivityHandlerResult<>("");
        result.setAutoNext(true);
        result.setOtherEntity(ruActivity);
        return result;
    }


    @Override
    protected void startNextActivity(ProcInstContext procContext, ProcRuActivity currentRuActivity) {
        endCurrentProcess();
        handlerSubProcess();
    }


    private void endCurrentProcess() {
        FlyQiuBeanManage procBean = procContext.getGlobalContext().getBeanManage();
        String procInstId = procContext.getProcInst().getId();

        HiProcInstMapper hiProcInstMapper = procBean.getMapper(HiProcInstMapper.class);
        RuProcInstMapper ruProcInstMapper = procBean.getMapper(RuProcInstMapper.class);
        ProcRuActivityMapper ruActivityMapper = procBean.getMapper(ProcRuActivityMapper.class);

        ProcHiInst procHiInst = hiProcInstMapper.selectById(procInstId);
        LambdaQueryWrapper<ProcRuActivity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ProcRuActivity::getProcInstId, procHiInst.getId());
        queryWrapper.eq(ProcRuActivity::getTenantId, procHiInst.getTenantId());
        Long count = ruActivityMapper.selectCount(queryWrapper);
        procHiInst.completeEndTime(LocalDateTime.now());
        ProcInstEventContext eventContext = EventTools.createContext(procContext, procHiInst);
        dispatch.completeBefore(eventContext);
        hiProcInstMapper.updateById(procHiInst);
        if (count == null || count < 1) {
            ruProcInstMapper.deleteById(procInstId);
        }
        flowVariableService.moveToHistory(procInstId);
        dispatch.completeAfter(eventContext);
    }

    /**
     * 结束子流程并返回父级流程 继续执行下一步流程
     */
    private void handlerSubProcess() {
        ProcRuInst procInst = procContext.getProcInst();
        if (StringTools.isEmpty(procInst.getParentId())) {
            return;
        }
        ProcessModel curProcessModel = procContext.getCurProcessModel();
        if (!(curProcessModel instanceof SubProcessModel subProcessModel)) {
            return;
        }
        RuProcInstMapper ruProcInstMapper = procBeanManage.getMapper(RuProcInstMapper.class);
        ProcRuInst parentInst = ruProcInstMapper.selectById(procInst.getParentId());
        if (parentInst == null) return;
        DefaultProcInstContext parentInstContext = createParentInstContext(subProcessModel, parentInst);
        ProcSequenceCompute nextProcSequenceCompute = ProcSequenceCompute.compute(scriptManage, parentInstContext.getCurProcessModel(), procInst.getProcDeployDefKey(), parentInstContext.getProcVariables());
        SequenceFlow finalSingle = nextProcSequenceCompute.getFinalSingle();
        FlyQiuFlowElementManage procElementManage = procContext.getGlobalContext().getFlowElementManage();
        ElementStartHandler actInst = procElementManage.createStartInst(finalSingle.getElementType());
        actInst.start(parentInstContext, finalSingle.getDefKey());
//        parentProcInstContext.setProcVariables();
    }

    private DefaultProcInstContext createParentInstContext(SubProcessModel subProcessModel, ProcRuInst parentInst) {
        FlyQiuFlowGlobalContext globalContext = procContext.getGlobalContext();

        FlyQiuFlowVarSerializeManage varSerializeManage = procContext.getGlobalContext().getFlowVarSerializeManage();

        ProcessModel parentProcessModel = subProcessModel.getParentProcessModel();

        DefaultProcInstContext parentContext = DefaultProcInstContext.create(procContext);
        parentContext.setProcInst(parentInst);
        parentContext.setGlobalContext(globalContext);
        parentContext.setCurProcessModel(parentProcessModel);
        FlowRuVariableServiceImpl procVariableService = procBeanManage.getBean(FlowRuVariableServiceImpl.class);
        List<Variable> procVariables = new ArrayList<>(procVariableService.getProcVariables(parentInst.getId()));
        VariableScope variableScope = new VariableScope(varSerializeManage, procVariables);
        parentContext.setProcVariables(variableScope);


        return parentContext;


    }

    private class ProcInstEventDispatch implements ProcInstEvent {

        @Override
        public void completeBefore(ProcInstEventContext eventContext) {
            procEventManage.triggerCompleteBeforeEvent(ProcInstEvent.class, eventContext);
        }

        @Override
        public void completeAfter(ProcInstEventContext eventContext) {
            procEventManage.triggerCompleteAfterEvent(ProcInstEvent.class, eventContext);
        }
    }
}
